/*
 * Copyright (c) 2020-2099 the original author or authors. All rights reserve.
 */
package org.tianyun.cloud.datasource.routing;

import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.pool.HikariPool;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.boot.jdbc.metadata.AbstractDataSourcePoolMetadata;
import org.springframework.cloud.util.ProxyUtils;
import org.tianyun.cloud.datasource.WrappedDataSource;

import javax.sql.DataSource;
import java.sql.SQLException;

/**
 * RoutingDataSourcePoolMetadata
 *
 * @author ebert_chan
 */
public class RoutingDataSourcePoolMetadata extends AbstractDataSourcePoolMetadata<DataSource> {

    public RoutingDataSourcePoolMetadata(DataSource dataSource) {
        super(dataSource);
    }

    @Override
    public Integer getActive() {
        try {
            return getHikariPool().getActiveConnections();
        } catch (Exception ex) {
            return null;
        }
    }

    @Override
    public Integer getIdle() {
        try {
            return getHikariPool().getIdleConnections();
        } catch (Exception ex) {
            return null;
        }
    }

    private HikariPool getHikariPool() {
        return (HikariPool) new DirectFieldAccessor(getDataSource()).getPropertyValue("pool");
    }


    @Override
    public Integer getMax() {
        return unwrap().getMaximumPoolSize();
    }


    @Override
    public Integer getMin() {
        return unwrap().getMinimumIdle();
    }

    @Override
    public String getValidationQuery() {
        return unwrap().getConnectionTestQuery();
    }

    @Override
    public Boolean getDefaultAutoCommit() {
        return unwrap().isAutoCommit();
    }

    private HikariDataSource unwrap() {
        DataSource dataSource = getDataSource();

        if (AopUtils.isAopProxy(dataSource) && (dataSource instanceof Advised)) {
            dataSource = ProxyUtils.getTargetObject(dataSource);
        }

        WrappedDataSource wrappedDataSource;
        try {
            RoutingDataSource routingDataSource = (RoutingDataSource) dataSource;
            wrappedDataSource = (WrappedDataSource) routingDataSource.resolveDataSource();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        return (HikariDataSource) wrappedDataSource.getDelegate();
    }

}
